'''
Created on 20-mei-2013

@author: Michael
'''
from preprocessing import *
from visualization import *
from imageReader import *
from os import listdir
import cv2
import cv2.cv as cv
import os
import numpy as np
import fnmatch
from numpy.oldnumeric.arrayfns import reverse

def preprocessImageForJawValley(img):
    (img,originalTop,originalLeft) = crop(img, 0.35, 0.35, 0.4, 0.2)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img = cv2.equalizeHist(img)
    return img

def preprocessImageForEdgeMap(img):
    (img,originalTop,originalLeft) = crop(img, 0.35, 0.35, 0.4, 0.2)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return (img,originalTop,originalLeft)
    
def getJaws(image):
    image = preprocessImageForJawValley(image)
    split = detectJawValleyIntervals(image, 25);
    upper = darkenTop(split,image)
    drawscaled(upper, 2)
    lower = darkenBottom(split, image)
    drawscaled(lower, 2)

def darkenTop(split, image):
    darkTopImage = np.zeros((image.shape[0],image.shape[1]), 'uint8')
    for (rSplit,cSplit) in split:
        for r in range(0,rSplit):
            darkTopImage[r,cSplit] = 0
        for r in range(rSplit,image.shape[0]):
            darkTopImage[r,cSplit] = image[r,cSplit]
    return darkTopImage

def darkenBottom(split, image): 
    darkBottomImage = np.zeros(image.shape, 'uint8')
    for (rSplit,cSplit) in split:
        for r in range(0,rSplit):
            darkBottomImage[r,cSplit] = image[r,cSplit]
        for r in range(rSplit,image.shape[0]):
            darkBottomImage[r,cSplit] = 0
    return darkBottomImage

def detectTeethValley(image):
    (preProcessedImage,originalTop,originalLeft) = preprocessImageForEdgeMap(image)
    edgeMap = closing(opening(adaptiveThreshold(preProcessedImage)))
    split = detectJawValleyIntervals(preprocessImageForJawValley(image),25)
    bestLinesTop = detectTeethValleyTop(edgeMap,split)
    
    segmentsTopLeft = cutAndCreateTopSegments(preProcessedImage,bestLinesTop,split)
    topTeethOriginalTopLeft = list()
    for (segmentTop,segmentLeft) in segmentsTopLeft:
        finalTop = originalTop + segmentTop
        finalLeft = originalLeft + segmentLeft
        topTeethOriginalTopLeft.append((finalTop,finalLeft))
    f = open("Teeth_coordinates/topteeth", 'w')
    for topLeft in topTeethOriginalTopLeft:
        f.write(str(int(topLeft[0])) + "," + str(int(topLeft[1])) + "\n")
    f.close()
    
    
    bestLinesBottom = detectTeethValleyBottom(edgeMap,split)
    segmentsTopLeft = cutAndCreateBottomSegments(preProcessedImage,bestLinesBottom,split)
    bottomTeethOriginalTopLeft = list()
    for (segmentTop,segmentLeft) in segmentsTopLeft:
        finalTop = originalTop + segmentTop
        finalLeft = originalLeft + segmentLeft
        bottomTeethOriginalTopLeft.append((finalTop,finalLeft))
    f = open("Teeth_coordinates/bottomteeth", 'w')
    for topLeft in bottomTeethOriginalTopLeft:
        f.write(str(int(topLeft[0])) + "," + str(int(topLeft[1])) + "\n")
    f.close()    


def detectTeethValleyTop(edgeMap,split):
    threshold = 120
    ignoreNext = 0
    lookNext = 0
    bestValue = -1
    bestLine = None
    bestLines = list()
    
    for (pixelRow, pixelColumn) in split:
        if ignoreNext > 0:
            ignoreNext -= 1
        else:
            (value,topColumn,pixels) = findLineIntervalTop(int(edgeMap.shape[0]*0.15), pixelRow, pixelColumn, edgeMap)
            if value >= threshold:
                if lookNext <= 0:
                    lookNext = 20
                    bestValue = -1
                    bestLine = None
                if value >= bestValue:
                    bestValue = value
                    bestLine = ((pixelColumn, pixelRow), (topColumn,int(edgeMap.shape[0]*0.15)) )
                lookNext -= 1
                if lookNext == 0:
                    bestLines.append(bestLine)
                    cv2.line(edgeMap, bestLine[0], bestLine[1], 175,  thickness=3)
                    ignoreNext += 70
    return bestLines
    
def detectTeethValleyBottom(edgeMap,split):
    threshold = 110
    ignoreNext = 0
    lookNext = 0
    bestValue = -1
    bestLine = None
    bestLines = list()
    
    for (pixelRow, pixelColumn) in split:
        if ignoreNext > 0:
            ignoreNext -= 1
        else:
            (value,bottomColumn,pixels) = findLineIntervalBottom(pixelRow, int(edgeMap.shape[0]*0.85), pixelColumn, edgeMap)
            if value >= threshold:
                if lookNext <= 0:
                    lookNext = 20
                    bestValue = -1
                    bestLine = None
                if value >= bestValue:
                    bestValue = value
                    bestLine = ((pixelColumn, pixelRow), (bottomColumn,int(edgeMap.shape[0]*0.85)) )
            lookNext -= 1
            if lookNext == 0:
                bestLines.append(bestLine)
                cv2.line(edgeMap, bestLine[0], bestLine[1], 175,  thickness=3)
                ignoreNext += 85
    return bestLines

def cutAndCreateTopSegments(image,bestLinesTop,split):
    topLeftCorners = list()
    for index,line1 in enumerate(bestLinesTop[:-1]):
        line2 = bestLinesTop[index+1]
        (leftColumn,rightColumn,bottomRow) = getBottomCornerPoints(line1,line2,split, image)
        cut = image[0:bottomRow+1,leftColumn:rightColumn+1]
        writeImage(cut, "top_teeth_" + str(index) + ".tif")
        topLeftCorners.append((0,leftColumn))
    return topLeftCorners
        
def cutAndCreateBottomSegments(image,bestLinesBottom,split):
    topLeftCorners = list()
    for index,line1 in enumerate(bestLinesBottom[:-1]):
        line2 = bestLinesBottom[index+1]
        (leftColumn,rightColumn,topRow) = getTopCornerPoints(line1,line2,split, image)
        cut = image[topRow:image.shape[0],leftColumn:rightColumn+1]
        writeImage(cut, "bottom_teeth_" + str(index) + ".tif")
        topLeftCorners.append((topRow,leftColumn))
    return topLeftCorners

def getBottomCornerPoints(line1,line2,split, image):
    #let line 1 start and end on first white appearance from each side
    r0b = line1[0][1]
    c0b = line1[0][0]
    r1t = line1[1][1]
    c1t = line1[1][0]
    pixels = pixelsThroughLine(r0b, c0b, r1t, c1t)
    #find first white pixel looking from middle (jawseparating line) to top
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newStart1 = pixels[i]
            break
    #find first white pixel looking from top to middle (jawsep line)
    pixels.reverse()
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newEnd1 = pixels[i]
            break
    #TUPLES ARE IMMUTABLE (so working with list now)
    newLine1 = [newStart1, newEnd1]
    leftColumn = newLine1[0][1]
    if(newLine1[1][1] < leftColumn):
        leftColumn = newLine1[1][1]
        
    #let line 2 start and end on first white appearance from each side
    r0b = line2[0][1]
    c0b = line2[0][0]
    r1t = line2[1][1]
    c1t = line2[1][0]
    pixels = pixelsThroughLine(r0b, c0b, r1t, c1t)
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newStart2 = pixels[i]
            break
    #find first white pixel looking from top to middle (jawsep line)
    pixels.reverse()
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newEnd2 = pixels[i]
            break
    newLine2 = [newStart2, newEnd2]
    #
    
    rightColumn = newLine2[0][1]
    if(newLine2[1][1] > rightColumn):
        rightColumn = newLine2[1][1]
    
    subSplit = split[split.index((line1[0][1],line1[0][0])):split.index((line2[0][1],line2[0][0]))+1]
    bottomRow = subSplit[0][0]
    for c in subSplit:
        if c[0] > bottomRow:
            bottomRow = c[0]        
    return (leftColumn,rightColumn,bottomRow)

def getTopCornerPoints(line1,line2,split, image):
    #let line 1 start and end on first white appearance from each side
    r0b = line1[0][1]
    c0b = line1[0][0]
    r1t = line1[1][1]
    c1t = line1[1][0]
    pixels = pixelsThroughLine(r0b, c0b, r1t, c1t)
    #find first white pixel looking from middle (jawseparating line) to top
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newStart1 = pixels[i]
            break
    #find first white pixel looking from top to middle (jawsep line)
    pixels.reverse()
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newEnd1 = pixels[i]
            break
    #TUPLES ARE IMMUTABLE (so working with list now)
    newLine1 = [newStart1, newEnd1]
    leftColumn = newLine1[0][1]
    if(newLine1[1][1] < leftColumn):
        leftColumn = newLine1[1][1]
        
    #let line 2 start and end on first white appearance from each side
    r0b = line2[0][1]
    c0b = line2[0][0]
    r1t = line2[1][1]
    c1t = line2[1][0]
    pixels = pixelsThroughLine(r0b, c0b, r1t, c1t)
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newStart2 = pixels[i]
            break
    #find first white pixel looking from top to middle (jawsep line)
    pixels.reverse()
    for i in range(len(pixels)):
        if image[pixels[i][0]][pixels[i][1]] != 0:
            newEnd2 = pixels[i]
            break
    newLine2 = [newStart2, newEnd2]
    
    rightColumn = newLine2[0][1]
    if(newLine2[1][1] > rightColumn):
        rightColumn = newLine2[1][1]
    
    subSplit = split[split.index((line1[0][1],line1[0][0])):split.index((line2[0][1],line2[0][0]))+1]
    topRow = subSplit[0][0]
    for c in subSplit:
        if c[0] < topRow:
            topRow = c[0]        
    return (leftColumn,rightColumn,topRow)
        
#returns the row in which the jaw valley lies for the given img
def getJawValleyFor(img):
    rowIntensities = np.zeros(img.shape[0])
    for row in range(img.shape[0]):
        rowSum = 0
        for column in range(img.shape[1]):
            rowSum += img[row, column]
        rowIntensities[row] = rowSum/img.shape[1]
            
    #find index of smallest value
    minVal = 99999
    minIndex = -1
    for i in range(len(rowIntensities)):
        if rowIntensities[i] < minVal:
            minVal = rowIntensities[i]
            minIndex = i
    return minIndex

#returns the rows on which the middle jaw valley lies
def detectJawValleyMiddleFor(img):
    (img,originalTop,originalLeft) = crop(img, 0.33, 0.33, 0, 0)
    return getJawValleyFor(img)
   
    
def detectJawValleyIntervals(img,intervals):
    intervalPct = float(1)/intervals
    valleyPoints = list()
    columns = img.shape[1]
    
    #draw line in middle interval
    middleValley = detectJawValleyMiddleFor(img)
    midLeft = int(math.floor(intervals/2)*intervalPct * columns)
    midRight =int((math.floor(intervals/2)+1)*intervalPct * columns)
    valleyPoints.extend(pixelsThroughLine(middleValley,midLeft,middleValley,midRight))
    
    #this loop will find the valleys right of the center
    fromValley = middleValley
    for j in range(int(math.floor(intervals/2)+1), int(intervals)):
        leftBound = int(j*intervalPct * columns)
        rightBound = int((j+1)*intervalPct * columns - 1)
        (endValley,pixels) = findLineIntervalRight(leftBound, rightBound, fromValley, img)
        valleyPoints.extend(pixels)
        fromValley = endValley
        
    #This loop will fin the valley left of the center
    endValley = middleValley
    for j in range(int(math.floor(intervals/2) - 1),-1,-1):
        leftBound = int(j*intervalPct * columns)
        rightBound = int((j+1)*intervalPct * columns - 1)
        (fromValley,pixels) = findLineInterValLeft(rightBound, leftBound, endValley, img)
        pixels.extend(valleyPoints)
        valleyPoints = pixels
        endValley = fromValley
    drawscaled(img,1)
    return valleyPoints
    
def findLineIntervalRight(left, right, fromValley, img):
    topPixelRight = fromValley - (right - left)
    bottomPixelRight = fromValley + (right - left)
    #endpoint of the darkest line
    darkestRow = -99999999
    #value of the darkest line
    darkestValue = 300
    
    darkestPixels = list()
    for p in range(topPixelRight, bottomPixelRight):
        pixelsToCheck = pixelsThroughLine(fromValley, left, p, right)
        val = 0
        for i in range(len(pixelsToCheck)):
            (r,c) = pixelsToCheck[i]
            val += img[r,c]
            
        avg = float(val)/len(pixelsToCheck)
        if avg <= darkestValue:
            darkestValue = avg
            darkestRow = p
            darkestPixels = pixelsToCheck
    return (darkestRow, darkestPixels)

def findLineInterValLeft(right, left, endValley, img):
    topPixelLeft = endValley - (right - left)
    bottomPixelLeft = endValley + (right - left)
    #endpoint of the darkest line
    darkestRow = -99999999
    #value of the darkest line
    darkestValue = 999999
    
    darkestPixels = list()
    for p in range(topPixelLeft, bottomPixelLeft):
        pixelsToCheck = pixelsThroughLine(p, left, endValley, right)
        val = 0
        for i in range(len(pixelsToCheck)):
            (r,c) = pixelsToCheck[i]
            val += img[r,c]
            
        avg = float(val)/len(pixelsToCheck)
        if avg <= darkestValue:
            darkestValue = avg
            darkestRow = p
            darkestPixels = pixelsToCheck
    return (darkestRow, darkestPixels)

def findLineIntervalTop(top, bottom, endValley, img):
    #we consider a max rotation of 22.5 degrees on the edge. 
    leftPixelTop = int(max(endValley - (bottom - top)*0.30,0))
    rightPixelTop = int(min(endValley + (bottom - top)*0.30,img.shape[1]))
    #endpoint of the darkest line
    brightestColumn = -1
    #value of the darkest line
    brightestValue = -1
    
    brightestPixels = list()
    for p in range(leftPixelTop, rightPixelTop):       
        pixelsToCheck = pixelsThroughLine(top, p, bottom, endValley)
        sorted(pixelsToCheck, key=lambda row: pixelsToCheck[0], reverse=True)
        val = 0
        whiteSeen = False
        length = 0
        for i in range(len(pixelsToCheck)):
            (r,c) = pixelsToCheck[i]
            if pixelsToCheck[i] > 0:
                whiteSeen = True
            if whiteSeen:
                val += img[r,c]
                length += 1
        avg = float(val)/length
        if avg >= brightestValue:
            brightestValue = avg
            brightestColumn = p
            brightestPixels = pixelsToCheck
    for i in range(len(brightestPixels)):
         
            (r,c) = brightestPixels[i]
           
    return (brightestValue, brightestColumn, brightestPixels)

def findLineIntervalBottom(top, bottom, endValley, img):
    #we consider a max rotation of 22.5 degrees on the edge. 
    leftPixelBottom = int(max(endValley - (bottom - top)*0.30,0))
    rightPixelBottom = int(min(endValley + (bottom - top)*0.30,img.shape[1]))
    #endpoint of the darkest line
    brightestColumn = -1
    #value of the darkest line
    brightestValue = -1
    
    brightestPixels = list()
    for p in range(leftPixelBottom, rightPixelBottom):
      
        pixelsToCheck = pixelsThroughLine(top, endValley, bottom, p)
     
        sorted(pixelsToCheck, key=lambda row: pixelsToCheck[0])
        
        val = 0
        whiteSeen = False
        length = 0
        for i in range(len(pixelsToCheck)):
            (r,c) = pixelsToCheck[i]
            if pixelsToCheck[i] > 0:
                whiteSeen = True
            if whiteSeen:
                val += img[r,c]
                length += 1
        avg = float(val)/length
        if avg >= brightestValue:
            brightestValue = avg
            brightestColumn = p
            brightestPixels = pixelsToCheck
    for i in range(len(brightestPixels)):
           
            (r,c) = brightestPixels[i]
          
    return (brightestValue, brightestColumn, brightestPixels)

def pixelsThroughLine(r0,c0, r1, c1):

    line = list()
    if c1 < c0:
        tempC = c1
        tempR = r1
        c1 = c0
        r1 = r0
        c0 = tempC
        r0 = tempR
    deltax = c1 - c0
    deltay = r1 - r0
    error = 0
    rmax = max(r0,r1)
    rmin = min(r0,r1)
    step = int(math.copysign(1,r1 - r0))
    if deltax == 0:
        for r in range(rmin,rmax+1):
            line.append((r,c0))
        return line
    deltaerr = abs(float(deltay) / float(deltax))
    r = r0
    for c in range(c0,c1+1):
        line.append((r,c))
        error = error + deltaerr
        while error >= 0.5 and r <= rmax and r >= rmin:
            r = r + step
            line.append((r,c))
            error = error - 1.0
    return line


